/********************************
* namespaces
*********************************/
var Narti = Narti || {};
Narti.Canvas = Narti.Canvas || {};

/********************************
* constructor
*********************************/
Narti.Canvas.Boltzman = function(jCtr, numMovers){
	
	this.canvas = document.createElement("canvas");
	if( this.canvas == null )return;
	this.context = this.canvas.getContext('2d');
	if( this.context == null )return;
	
	jCtr.append(this.canvas);
	
	this.offsetX = jCtr.offset().left;
	this.offsetY = jCtr.offset().top;
	
	this.canvas.width = jCtr.width();
	this.canvas.height = jCtr.height();

	this.marble = new Narti.Canvas.Marbling(numMovers, this.canvas);			  			 
		
};

Narti.Canvas.Boltzman.prototype = {
	animate : function(){
		this.intervalID = setInterval(
			(function(self) {
				 	 return function() {self.draw();
		    	 }
			})(this), this.interval);  
		
	},
	draw: function(){
		this.update();
		this.onTestWalls();
		this.onCollide();
		this.render();
	},
	update : function(){
		this.marble.update();
	},
	onTestWalls : function(){
		this.marble.onTestWalls(this.canvas);
	},
	onCollide : function(){
		this.marble.onCollide();
	},
	render : function(){
		var ball;
		var balls = this.marble.getMarbles();
		
		this.context.globalCompositeOperation = "source-over";
		this.context.clearRect( 0 , 0 , this.canvas.width , this.canvas.height );
	
		this.context.globalCompositeOperation = "lighter";
		
		for (var i =0; i < balls.length; i++) {
			ball = balls[i];
			ball.x = ball.nextx;
			ball.y = ball.nexty;
			
			
			this.context.fillStyle = ball.color;
			this.context.beginPath();
			this.context.arc(ball.x,ball.y,ball.radius,0,Math.PI*2,true);
			this.context.closePath();
			this.context.fill();
			
			
		}
	}
	
};

Narti.Canvas.Marbling = function(numMarbles, canvas){
	var x, y, speed, angle, radians, vx, vy;
	var radius = 5;
	var maxSize = 4;
	var minSize = 2;
	
	var placeOK = false;
	var ball;
	this.balls = [];
	for(var i = 0; i < numMarbles; i++){
		placeOK = false;
		while (!placeOK) {
			radius = Math.floor(Math.random()*maxSize)+minSize;
			x = radius*3 + (Math.floor(Math.random()*canvas.width)-radius*3);
			y = radius*3 + (Math.floor(Math.random()*canvas.height)-radius*3);
			speed = Math.floor( 4 / maxSize) + 2;
			angle =  Math.floor(Math.random()*360);
			radians = angle * Math.PI/ 180;
			vx = Math.cos(radians) * speed;
			vy = Math.sin(radians) * speed;
			
			ball = {x:x,y:y, nextX:x, nextY:y, radius:radius, speed:speed, angle:angle, vx:vx, vy:vy, mass:radius};
			placeOK = this.canStartHere(ball);
		}
		ball.color = "rgb(" + Math.floor( Math.random()*255 ) + "," + Math.floor( Math.random()*255 ) + "," + Math.floor( Math.random()*255 ) + ")";
		this.balls.push(ball);
		
	}
};

Narti.Canvas.Marbling.prototype =  {	
	canStartHere : function(ball){
		var result = true;
		for (var i =0; i <this.balls.length; i++) {
			if(this.hitTestCircle(ball, this.balls[i])) {
				result = false;
				break;
			}
		}
		return result;
	},
	hitTestCircle : function(ball1, ball2){
		var result = false;
		var dx = ball1.nextx - ball2.nextx;
		var dy = ball1.nexty - ball2.nexty;
		var distance = (dx * dx + dy * dy);
    	if (distance <= (ball1.radius + ball2.radius) * (ball1.radius + ball2.radius) ) {
     	      result = true;
     	}
     	return result;
	},
	getMarbles : function(){
		return this.balls;
	},
	update : function() {
		for (var i =0; i <this.balls.length; i++) {
			var ball = this.balls[i];
			ball.nextx = (ball.x += ball.vx);
			ball.nexty = (ball.y += ball.vy);
		}
	},
	onTestWalls : function(canvas) {
		var ball;
				
		for (var i =0; i <this.balls.length; i++) {
			ball = this.balls[i];
			
			if (ball.nextx+ball.radius > canvas.width) {
				ball.vx = ball.vx*-1;
				ball.nextx = canvas.width - ball.radius;
				
			} else if (ball.nextx-ball.radius < 0 ) {
				ball.vx = ball.vx*-1;
				ball.nextx =  ball.radius;
			
			} else if (ball.nexty+ball.radius > canvas.height ) {
				ball.vy = ball.vy*-1;
				ball.nexty = canvas.height - ball.radius;
				
			} else if(ball.nexty-ball.radius < 0) {
				ball.vy = ball.vy*-1;
				ball.nexty =  ball.radius;
			}
			
		}
	
	},
	onCollide : function() {
            var ball;
            var testBall;
            for (var i =0; i <this.balls.length; i++) {
                    ball = this.balls[i];
                    for (var j = i+1; j < this.balls.length; j++) {
                        testBall = this.balls[j];
                        if (this.hitTestCircle(ball,testBall)) {
                            this.onCollideBalls(ball,testBall);
                        }
                    }
            }
  	},
	onCollideBalls : function(ball1,ball2) {
                
                var color = (ball1.color + ball2.color / 2 );
                ball1.color = ball2.color = color;
                
 		var dx = ball1.nextx - ball2.nextx;
		var dy = ball1.nexty - ball2.nexty;
 
		var collisionAngle = Math.atan2(dy, dx);
 
		var speed1 = Math.sqrt(ball1.vx * ball1.vx + ball1.vy * ball1.vy);
		var speed2 = Math.sqrt(ball2.vx * ball2.vx + ball2.vy * ball2.vy);
 
		var direction1 = Math.atan2(ball1.vy, ball1.vx);
		var direction2 = Math.atan2(ball2.vy, ball2.vx);
 
		var vx_1 = speed1 * Math.cos(direction1 - collisionAngle);
		var vy_1 = speed1 * Math.sin(direction1 - collisionAngle);
		var vx_2 = speed2 * Math.cos(direction2 - collisionAngle);
		var vy_2 = speed2 * Math.sin(direction2 - collisionAngle);
		
		var final_vx_1 = ((ball1.mass - ball2.mass) * vx_1 + (ball2.mass + ball2.mass) * vx_2)/(ball1.mass + ball2.mass);
		var final_vx_2 = ((ball1.mass + ball1.mass) * vx_1 + (ball2.mass - ball1.mass) * vx_2)/(ball1.mass + ball2.mass);
 
		var final_vy_1 = vy_1;
		var final_vy_2 = vy_2;
 
		ball1.vx = Math.cos(collisionAngle) * final_vx_1 + Math.cos(collisionAngle + Math.PI/2) * final_vy_1;
		ball1.vy = Math.sin(collisionAngle) * final_vx_1 + Math.sin(collisionAngle + Math.PI/2) * final_vy_1;
		ball2.vx = Math.cos(collisionAngle) * final_vx_2 + Math.cos(collisionAngle + Math.PI/2) * final_vy_2;
		ball2.vy = Math.sin(collisionAngle) * final_vx_2 + Math.sin(collisionAngle + Math.PI/2) * final_vy_2;
 
  		ball1.nextx = (ball1.nextx += ball1.vx);
		ball1.nexty = (ball1.nexty += ball1.vy);
		ball2.nextx = (ball2.nextx += ball2.vx);
		ball2.nexty = (ball2.nexty += ball2.vy);
	}
	
	
};



